package com.gaojun;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

import com.gaojun.tool.Constant;

public class AudioRecord {
	// 定义录音格式
	AudioFormat af = null;
	// 定义目标数据行,可以从中读取音频数据,该 TargetDataLine 接口提供从目标数据行的缓冲区读取所捕获数据的方法。
	TargetDataLine td = null;
	// 定义源数据行,源数据行是可以写入数据的数据行。它充当其混频器的源。应用程序将音频字节写入源数据行，这样可处理字节缓冲并将它们传递给混频器。
	SourceDataLine sd = null;
	// 定义字节数组输入输出流
	ByteArrayInputStream bais = null;
	ByteArrayOutputStream baos = null;
	// 定义音频输入流
	AudioInputStream ais = null;
	// 定义停止录音的标志，来控制录音线程的运行
	Boolean stopflag = false;
	// 记录开始录音的时间
	long startPlay;
	// 设置一个播放的标志
	Boolean playflag;
	// 定义音频波形每次显示的字节数
	int intBytes = 0;
	// 定义每次录音的时候每次提取字节来画音频波
	byte audioDataBuffer[] = null;
	// 设置画波形线程的终止的标志
	int cnt;
	// 定义播放录音时一个缓冲数组
	byte btsPlay[] = null;
	File tarFile = null;

	public AudioRecord(File file) {
		// TODO Auto-generated constructor stub
		this.tarFile = file;
	}

	// 开始录音
	public void capture() {
		try {
			// af为AudioFormat也就是音频格式
			af = getAudioFormat();
			DataLine.Info info = new DataLine.Info(TargetDataLine.class, af);
			td = (TargetDataLine) (AudioSystem.getLine(info));

			// 打开具有指定格式的行，这样可使行获得所有所需的系统资源并变得可操作。
			td.open(af);
			// 允许某一数据行执行数据 I/O
			td.start();

			Record record = new Record();
			Thread t1 = new Thread(record);
			t1.start();
		} catch (Exception ex) {
			ex.printStackTrace();
			return;
		}
	}

	// 保存录音
	public void save() {
		stopflag = true;
		af = getAudioFormat();
		byte audioData[] = baos.toByteArray();
		bais = new ByteArrayInputStream(audioData);
		ais = new AudioInputStream(bais, af, audioData.length / af.getFrameSize());
		// 定义最终保存的文件名
		File file = null;
		// 写入文件
		try {
			// 以当前的时间命名录音的名字
			// 将录音的文件存放到F盘下语音文件夹下
			File filePath = new File(Constant.SAVE_PATH);
			if (!filePath.exists()) {// 如果文件不存在，则创建该目录
				filePath.mkdirs();
			}
			long time = System.currentTimeMillis();
			file = new File(filePath + "/" + time + ".wav");
			AudioSystem.write(ais, AudioFileFormat.Type.WAVE, file);
			// 将录音产生的wav文件转换为容量较小的mp3格式
			Runtime run = null;
			// 测试当前的路径

			try {
				run = Runtime.getRuntime();
				// 调用编码器来将wav文件转换为mp3文件
				// 把编码得到的mp3文件先存放到D盘下，然后利用文件拷贝函数将它放到指定的文件夹下同时将D盘下的文件删除
				Process p = run.exec(filePath + "/" + "lame -b 16 " + filePath + "/" + file.getName() + " "
						+ tarFile.getPath().toString()); // 16为码率，可自行修改
				// 释放进程
				p.getOutputStream().close();
				p.getInputStream().close();
				p.getErrorStream().close();
				// 等待
				p.waitFor();

				// 删除之前保存的的wav文件
				if (file.exists()) {
					file.delete();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				// 最后都要执行的语句
				// run调用lame解码器最后释放内存
				run.freeMemory();
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭流
			try {

				if (bais != null) {
					bais.close();
				}
				if (ais != null) {
					ais.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	// 设置AudioFormat的参数
	public AudioFormat getAudioFormat() {
		// 下面注释部分是另外一种音频格式，两者都可以
		AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
		float rate = 8000f;
		int sampleSize = 16;
		String signedString = "signed";
		boolean bigEndian = true;
		int channels = 1;
		return new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian);
		// //采样率是每秒播放和录制的样本数
		// float sampleRate = 16000.0F;
		// // 采样率8000,11025,16000,22050,44100
		// //sampleSizeInBits表示每个具有此格式的声音样本中的位数
		// int sampleSizeInBits = 16;
		// // 8,16
		// int channels = 1;
		// // 单声道为1，立体声为2
		// boolean signed = true;
		// // true,false
		// boolean bigEndian = true;
		// // true,false
		// return new AudioFormat(sampleRate, sampleSizeInBits, channels,
		// signed,bigEndian);
	}

	// 录音类，因为要用到MyRecord类中的变量，所以将其做成内部类
	class Record implements Runnable {
		// 定义存放录音的字节数组,作为缓冲区
		byte bts[] = new byte[10000];

		// 将字节数组包装到流里，最终存入到baos中
		// 重写run函数
		public void run() {
			baos = new ByteArrayOutputStream();
			try {
				stopflag = false;
				while (stopflag != true) {
					// 当停止录音没按下时，该线程一直执行
					// 从数据行的输入缓冲区读取音频数据。
					// 要读取bts.length长度的字节,cnt 是实际读取的字节数
					int cnt = td.read(bts, 0, bts.length);
					if (cnt > 0) {
						baos.write(bts, 0, cnt);
					}

					// 开始从音频流中读取字节数
					byte copyBts[] = bts;
					bais = new ByteArrayInputStream(copyBts);
					ais = new AudioInputStream(bais, af, copyBts.length / af.getFrameSize());
					try {
						DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, af);
						sd = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
						sd.open(af);
						sd.start();

						// 从音频流中读取
						int Buffer_Size = 10000;
						audioDataBuffer = new byte[Buffer_Size];
						int outBytes;

						intBytes = ais.read(audioDataBuffer, 0, audioDataBuffer.length);

						// 不写到混频器中这样就不会播放
						// if (intBytes >= 0) {
						// outBytes = sd.write(audioDataBuffer, 0,audioDataBuffer.length);
						// }
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					// intBytes = -1;
					// 关闭打开的字节数组流
					if (baos != null) {
						baos.close();
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					// 下面这句td.drain()不能要，这样如果不播放数据就阻塞再次录音会出现其他程序访问错误
					// td.drain();
					td.close();
				}
			}
		}

	}

}
